﻿using System;
using System.Diagnostics;
using SfsTools.Core;
using mindvnas.Common;

namespace SfsTools.List
{
    /// <summary>
    /// Implements the <em>format</em> application.
    /// </summary>
    public class Program : ConsoleBase
    {
        #region Properties

        /// <summary>
        /// Contains the path to the image file to open.
        /// </summary>
        private String FImageFile;
        /// <summary>
        /// Contains the virtual path to list.
        /// </summary>
        private String FVirtualPath;
        /// <summary>
        /// Controls whether to list subdirectories as well.
        /// </summary>
        private bool FRecurse;

        #endregion

        #region Methods

        #region Entry point
        /// <summary>
        /// Entry point.
        /// </summary>
        /// <param name="arguments"></param>
        [DebuggerStepThrough]
        static void Main(String[] arguments)
        {
            new Program().Process(arguments);
        }
        #endregion

        #region Arguments

        /// <summary>
        /// Invoked before processing arguments.
        /// </summary>
        protected override void OnBeforeProcessArguments()
        {
            FVirtualPath = new String(System.IO.Path.DirectorySeparatorChar, 1);
        }

        /// <summary>
        /// Invoked after processing arguments.
        /// </summary>
        protected override void OnAfterProcessArguments()
        {
            if (FVirtualPath != null)
            {
                // If not an absolute path, assume it is an absolue path.
                if (!FVirtualPath.StartsWith(new String(System.IO.Path.DirectorySeparatorChar, 1)))
                    FVirtualPath = String.Concat(System.IO.Path.DirectorySeparatorChar, FVirtualPath);
            }
        }

        /// <summary>
        /// Processes the specified command-line argument.
        /// </summary>
        /// <param name="argument">The command-line argument.</param>
        protected override void ProcessArgument(ConsoleBaseArgument argument)
        {
            if (argument.Name == "-image")
            {
                FImageFile = argument.Value;
                Arguments.Add(argument);
            }
            else if (argument.Name == "-path")
            {
                FVirtualPath = argument.Value;
            }
            else if (argument.Name == "-r")
            {
                FRecurse = true;
            }
            else
            {
                base.ProcessArgument(argument);
            }
        }

        /// <summary>
        /// Displays an usage notice.
        /// </summary>
        [DebuggerStepThrough]
        protected override void UsageParameters()
        {
            base.UsageParameters();
            //
            UsageParameter("-image", Translation.Resources.GetString("ArgI"));
            UsageParameter("-path", Translation.Resources.GetString("ArgP"));
            UsageParameter("-r", Translation.Resources.GetString("ArgR"));
        }

        #endregion

        /// <summary>
        /// Performs whatever the application does.
        /// </summary>
        protected override void ProcessCore()
        {
#if DEBUG
            String pathToVolume = @"E:\Work\VisualStudio\SfsTools\Core\";
            FImageFile = pathToVolume + "disk";
            FRecurse = true;
#endif
            if (HelpRequested || FImageFile == null || FVirtualPath == null)
            {
                HelpRequested = true;
                base.ProcessCore();
            }
            else
            {
                using (FileBackedVolume volume = FileBackedVolume.Open(FImageFile))
                {
                    using (TransactionalVolume trv = new TransactionalVolume(volume))
                    {
                        using (FileSystem fs = new FileSystem(trv))
                        {
                            // Get root directory.
                            IDirectory root = fs.RootDirectory;
                            IFileSystemObject fso = root.Resolve(FVirtualPath);
                            fso.AcceptVisitor(new ListVisitor(this));
                        }
                    }
                }
#if DEBUG
                Console.ReadLine();
#endif
            }
        }

        #endregion

        #region Constructors
        /// <overloads>Initializes a new instance of <see cref="Program"/>.</overloads>
        /// <summary>
        /// Initializes a new instance of <see cref="Program"/>.
        /// </summary>
        [DebuggerStepThrough]
        public Program()
            : base()
        {
        }
        #endregion

        #region Helper classes

        /// <summary>
        /// Visitor for <see cref="IFileSystemObject"/>s that displays information about them on the console.
        /// </summary>
        private class ListVisitor : Object, IFileSystemObjectVisitor
        {
            #region Properties

            /// <summary>
            /// Contains the parent instance.
            /// </summary>
            private readonly Program FParent;

            #endregion

            #region Constructors
            /// <overloads>Initializes a new instance of <see cref="ListVisitor"/>.</overloads>
            /// <summary>
            /// Initializes a new instance of <see cref="ListVisitor"/>.
            /// </summary>
            /// <param name="parent">The parent instance.</param>
            [DebuggerStepThrough]
            public ListVisitor(Program parent)
                : base()
            {
                FParent = parent;
            }
            #endregion

            #region IFileSystemObjectVisitor Members

            /// <summary>
            /// Called by <see cref="IDirectory"/> objects.
            /// </summary>
            /// <param name="directory">The directory.</param>
            [DebuggerStepThrough]
            void IFileSystemObjectVisitor.VisitDirectory(IDirectory directory)
            {
                // Write header.
                FParent.WriteLine(Translation.Resources.GetString(new String('A', 1)), directory.GetFullPath());
                // List objects.
                foreach (IFileSystemObject fso in directory.GetObjects())
                {
                    fso.AcceptVisitor(new InnerVisitor(FParent));
                }
                // Recurse.
                if (FParent.FRecurse)
                {
                    foreach (IDirectory dir in directory.GetDirectories())
                    {
                        FParent.WriteLine();
                        dir.AcceptVisitor(this);
                    }
                }
            }

            /// <summary>
            /// Called by <see cref="IFile"/> objects.
            /// </summary>
            /// <param name="file">The file.</param>
            [DebuggerStepThrough]
            void IFileSystemObjectVisitor.VisitFile(IFile file)
            {
                // Write header.
                FParent.WriteLine(Translation.Resources.GetString(new String('A', 1)), file.GetFullPath());
                file.AcceptVisitor(new InnerVisitor(FParent));
            }

            #endregion

            #region Helper classes

            /// <summary>
            /// Displays information about a single <see cref="IFileSystemObject"/>.
            /// </summary>
            private class InnerVisitor : Object, IFileSystemObjectVisitor
            {
                #region Properties

                /// <summary>
                /// Contains the parent instance.
                /// </summary>
                private readonly Program FParent;

                #endregion

                #region Constructors
                /// <overloads>Initializes a new instance of <see cref="InnerVisitor"/>.</overloads>
                /// <summary>
                /// Initializes a new instance of <see cref="InnerVisitor"/>.
                /// </summary>
                /// <param name="parent">The parent instance.</param>
                [DebuggerStepThrough]
                public InnerVisitor(Program parent)
                    : base()
                {
                    FParent = parent;
                }
                #endregion

                #region IFileSystemObjectVisitor Members

                /// <summary>
                /// Called by <see cref="IDirectory"/> objects.
                /// </summary>
                /// <param name="directory">The directory.</param>
                [DebuggerStepThrough]
                void IFileSystemObjectVisitor.VisitDirectory(IDirectory directory)
                {
                    String updateStr = directory.LastUpdate.ToString();
                    String lengthStr = Translation.Resources.GetString(new String('D', 1));
                    // ReSharper disable PossibleNullReferenceException
                    FParent.WriteLine(Translation.Resources.GetString(new String('F', 1)),
                        String.Concat(' ', ' ', directory.Name).PadRight(79 - 2 - 2 - updateStr.Length - lengthStr.Length),
                        lengthStr, updateStr);
                    // ReSharper restore PossibleNullReferenceException
                }

                /// <summary>
                /// Called by <see cref="IFile"/> objects.
                /// </summary>
                /// <param name="file">The file.</param>
                [DebuggerStepThrough]
                void IFileSystemObjectVisitor.VisitFile(IFile file)
                {
                    String updateStr = file.LastUpdate.ToString();
                    String lengthStr = file.Length.ToString();
                    FParent.WriteLine(Translation.Resources.GetString(new String('F', 1)),
                        String.Concat(' ', ' ', file.Name).PadRight(79 - 2 - 2 - updateStr.Length - lengthStr.Length),
                        lengthStr, updateStr);
                }

                #endregion
            }

            #endregion
        }

        #endregion
    }
}
